package com.whitecat.voting.web;

import com.whitecat.voting.common.Resp;
import com.whitecat.voting.constant.RespCode;
import com.whitecat.voting.coverter.MpUserInfoConverter;
import com.whitecat.voting.dto.DecryptDataParam;
import com.whitecat.voting.dto.LoginSucc;
import com.whitecat.voting.dto.wx.MpPhoneData;
import com.whitecat.voting.dto.MpRegisterParam;
import com.whitecat.voting.dto.wx.MpUserInfoDTO;
import com.whitecat.voting.dto.wx.SessionKey;
import com.whitecat.voting.security.WxCodeToken;
import com.whitecat.voting.service.MpUserInfoService;
import com.whitecat.voting.service.WxApiService;
import com.whitecat.voting.service.WxTokenMgrService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@Api(value = "用户接口", tags = {"用户接口"})
@Slf4j
@RestController
@RequestMapping("/user/")
public class UserController extends BaseController {

    private final WxApiService wxApiService;

    private final WxTokenMgrService wxTokenMgrService;

    private final MpUserInfoService mpUserInfoService;

    public UserController(WxApiService wxApiService, WxTokenMgrService wxTokenMgrService, MpUserInfoService mpUserInfoService) {
        this.wxApiService = wxApiService;
        this.wxTokenMgrService = wxTokenMgrService;
        this.mpUserInfoService = mpUserInfoService;
    }

    @ApiOperation(value = "用户注册", notes = "用户注册，并返回openid。")
    @PostMapping(value = "/reg", produces = MediaType.APPLICATION_JSON_VALUE)
    public Resp<?> reg(@ApiParam(value = "加密数据") @RequestBody @Valid MpRegisterParam params) {
        SessionKey sk = wxTokenMgrService.getSessionKeyByCode(params.getCode());
        if (sk == null) {
            return fail();
        }

        String openId = sk.getOpenId();
        log.debug("decrypting for: [{}]", openId);
        DecryptDataParam ddp = new DecryptDataParam();
        ddp.setIv(params.getIv());
        ddp.setData(params.getData());
        ddp.setSessionKey(sk);
        MpUserInfoDTO mpUserInfoDTO = wxApiService.decryptUserInfo(ddp);

        // 持久化用户解密数据
        mpUserInfoService.addOrModifyUserInfo(MpUserInfoConverter.INSTANCE.toEntity(mpUserInfoDTO));

        // 注册后自动登录
        SecurityUtils.getSubject().login(new WxCodeToken(openId));
        return ok(new LoginSucc(openId, SecurityUtils.getSubject().getSession().getId().toString()));
    }

    @ApiOperation(value = "用户登录", notes = "小程序用户登录，并返回openid。")
    @PostMapping(value = "/login", produces = MediaType.APPLICATION_JSON_VALUE)
    public Resp<?> login(@RequestParam String code) {
        SessionKey sk = wxTokenMgrService.getSessionKeyByCode(code);
        if (sk == null || sk.getOpenId() == null) {
            return fail(RespCode.UNAUTHORIZED);
        }

        // 且能够从数据库获取到对应解密数据即为登录成功
        String openId = sk.getOpenId();
        SecurityUtils.getSubject().login(new WxCodeToken(openId));
        return ok(new LoginSucc(openId, SecurityUtils.getSubject().getSession().getId().toString()));
    }

    @ApiOperation(value = "刷新SessionKey", notes = "获取SessionKey并写入缓存，返回openid。非登录场景解密前需调用该接口，并在前端保存openid。")
    @PostMapping(value = "/sessionKey")
    public String sessionKey(@RequestParam String code) {
        SessionKey sk = wxTokenMgrService.getSessionKeyByCode(code);
        return sk == null ? null : sk.getOpenId();
    }

    @ApiOperation(value = "解密手机数据", notes = "解密小程序手机数据。非登录场景解密前需调用sessionKey接口。")
    @PostMapping(value = "/decryptPhone")
    Resp<MpPhoneData> decryptPhone(@ApiParam(value = "加密数据及参数") @Valid @RequestBody DecryptDataParam params) {
        return ok(wxApiService.decryptPhone(params));
    }

    @ApiOperation(value = "解密用户数据", notes = "解密小程序用户数据。非登录场景解密前需调用sessionKey接口")
    @PostMapping(value = "/decryptUserInfo")
    Resp<MpUserInfoDTO> decryptUserInfo(@ApiParam(value = "加密数据及参数") @Valid @RequestBody DecryptDataParam params) {
        return ok(wxApiService.decryptUserInfo(params));
    }
}
